﻿using System;

namespace SQLPXBase.Factory.Keys
{
    public abstract class BaseExtensionKey<PX>
        where PX : SQLProxyBase, new()
    {
    }

    public class ExtensionKey<T, PX> : BaseExtensionKey<PX>
        where PX : SQLProxyBase, new()
        where T : FactorizableModel<PX>
    {
        private FactorizableModel<PX> mainModel;
        protected  internal T model = null;

        public bool ThrowExceptionIfLazyIsMissing { get; set; }
        public bool AlwaysRetryLazyLoading { get; set; }
        protected bool lazyLoadingConsumed = false;

        #region Events

        public event ExtensionKeyEventHandler OnLazyMissing;
        public event ExtensionKeyEventHandler OnLazyLoading;
        public delegate void ExtensionKeyEventHandler(ExtensionKey<T, PX> extensionKey);

        #endregion

        public ExtensionKey(FactorizableModel<PX> mainModel)
        {
            if (mainModel == null)
                throw new Exception("Cannot initialize extension key for null object");

            this.mainModel = mainModel;
            ThrowExceptionIfLazyIsMissing = false;
            AlwaysRetryLazyLoading = false;
        }

        public virtual T Model
        {
            get
            {
                if (model == null)
                {
                    try
                    {
                        if (AlwaysRetryLazyLoading || !lazyLoadingConsumed)
                        {
                            lazyLoadingConsumed = true;
                            if (OnLazyLoading != null)
                                OnLazyLoading(this);
                            else
                                model = mainModel.factory.Load<T>(MainKey);
                        }
                    }
                    catch (Exceptions.RecordNotFoundException ex)
                    {
                        // if cannot load lazy object, throws exception only if flag is set
                        if (ThrowExceptionIfLazyIsMissing)
                            throw;
                    }

                    if (model == null)
                        if (OnLazyMissing != null)
                            OnLazyMissing(this);
                }

                return model;
            }
        }

        public virtual void SetModel(T model)
        {
            if (!model.GetPK().IsEmpty && model.GetPK() != mainModel.GetPK())
                throw new Exception("Cannot set model in Extension Key: keys do not match");

            this.model = model;
            lazyLoadingConsumed = false;
        }

        public ModelFactory<PX> Factory
        {
            get
            {
                return mainModel == null ? null : mainModel.factory;
            }
        }

        public PrimaryKey MainKey
        {
            get
            {
                if (mainModel != null) 
                    return mainModel.GetPK();

                throw new Exception("Mainmodel is not initialized");
            }
        }

        public void RefreshLazy()
        {
            model = null;
            lazyLoadingConsumed = false;
        }
    }

    public class ExtensionModel<T, PX> : ExtensionKey<T, PX>
        where PX : SQLProxyBase, new()
        where T : FactorizableModel<PX>
    {
        #region Events

        public new event ExtensionKeyEventHandler OnLazyMissing;
        public new event ExtensionKeyEventHandler OnLazyLoading;
        //public delegate void ExtensionKeyEventHandler(ExtensionKey<T, PX> extensionKey);

        #endregion

        public ExtensionModel(FactorizableModel<PX> mainModel) : base(mainModel)
        {
        }

        public override void SetModel(T model)
        {
            this.model = model;
            lazyLoadingConsumed = false;
        }

        public override T Model
        {
            get
            {
                if (model == null)
                {
                    if (AlwaysRetryLazyLoading || !lazyLoadingConsumed)
                    {
                        lazyLoadingConsumed = true;
                        if (OnLazyLoading != null)
                            OnLazyLoading(this);
                    }

                    if (model == null)
                    {
                        if (ThrowExceptionIfLazyIsMissing)
                            throw new Exception(string.Format("Extension model of type {0} is missing",
                                typeof (T).FullName));

                        if (OnLazyMissing != null)
                            OnLazyMissing(this);
                    }
                }

                return model;
            }
        }
    }
}
